#pragma once

#include "zen_vap_helper.h"
#include "zen_vap_node.h"
#include "zen_vap_texture.h"
#include "zen_point.h"
#include "zen_exception.h"
#include <vector>
#include <mutex>

namespace Zen { namespace Vap {
	
	template<class _VertexData>
	class FigureContent : public virtual Object
	{
	public:
		typedef _VertexData VertexData;

	public:
		FigureContent()
		{
		}
		virtual ~FigureContent() = default;
		
		void addVertex(VertexData const & data) {
			m_vertexes.push_back(data);
			m_is_vertexes_dirty = true;
		}
		void addVertexes(VertexData const * src, size_t count) {
			m_vertexes.insert(m_vertexes.end(), src, src + count);
			m_is_vertexes_dirty = true;
		}
		void updateVertex(size_t index, VertexData const & data) {
			m_vertexes[index] = data;
			m_is_vertexes_dirty = true;
		}
		void updateVertexes(size_t index, VertexData const * src, size_t count) {
			musts(index + count <= m_vertexes.size(), "out of range");
			for(size_t i = 0; i < count; ++i)
			{
				m_vertexes[i+index] = *src++;
			}
			m_is_vertexes_dirty = true;
		}
		void removeAllVertexes()
		{
			m_vertexes.clear();
		}

		VertexData const & getVertex(size_t index) const {
			return m_vertexes[index];
		}

		size_t getVertexesCount()
		{
			return m_vertexes.size();
		}

		DrawBuffer<VertexData> & getDrawBuffer() {

			if(m_is_vertexes_dirty) {
				mRBuffer.v.updateBuffer(m_vertexes.data(), m_vertexes.size());
				m_is_vertexes_dirty = false;
			}
			return mRBuffer;
		}


		ZEN_MEMBER_COPY_GET_SET(bool, m_is_strip, isStrip, setStrip);
	protected:
		DrawBuffer<VertexData> mRBuffer;

		std::vector<VertexData> m_vertexes;
		bool m_is_vertexes_dirty = true;
	};

	/// figure
	class Figure : public Node2d, public PartWithMatrix2d, public PartWithColor
	{
	protected:
		Figure()
		: Node2d()
		{

		}

		virtual void updateContent() {}
	};
	/// points
	/// ===========
	using FigurePointsContent = FigureContent<VertexDataPoints>;
	using SharedFigurePointsContent = std::shared_ptr<FigurePointsContent>;

	class FigurePoints : public Figure
	{
	public:

		FigurePoints();

		/// point texture.
		void setTexture(SharedTexture texture);
		
		SharedTexture getTexture();

		/// point size
		void setPointSizeScale(float scale);

		float getPointSizeScale();

		/// content
		void setFigureContent(SharedFigurePointsContent content) { m_content = content; }

		auto getFigureContent() { return m_content; }

		/// shader
		void setShaderSelector(ShaderKitPoints::SelectFunction s) { m_shaders = s; }

		auto getShaderSelector() { return m_shaders; }

		/// update
		virtual void updateContent() override;

		virtual void draw() override;

	protected:
		SharedFigurePointsContent m_content;

		SharedTexture m_texture;

		Vec4 m_texture_rect_converted;

		ShaderKitPoints::SelectFunction m_shaders;

		Vec2 m_size_ratio; // auto calc
		void resetSizeRatio_();
		float m_point_scale = 1.f; // set

		void initR_();
		void drawR_();
	};
	/// color
	/// ===========
	using FigureColorsContent = FigureContent<VertexDataColors>;
	using SharedFigureColorsContent = std::shared_ptr<FigureColorsContent>;

	class FigureColors : public Figure
	{
	public:
		FigureColors();
		
		virtual void draw() override;

		/// mode
		void setStrip(bool);

		bool isStrip();

		void setLineMode(bool);

		bool isLineMode();

		/// content size
		void setContentSize(Size2 size);
		
		Size2 getContentSize();

		/// content
		void setFigureContent(SharedFigureColorsContent content) { m_content = content; }

		auto getFigureContent() { return m_content; }

		/// shader
		void setShaderSelector(ShaderKitColors::SelectFunction s) { m_shaders = s; }

		auto getShaderSelector() { return m_shaders; }
		
	protected:
		SharedFigureColorsContent m_content;

		ShaderKitColors::SelectFunction m_shaders;
		
		bool m_is_line_mode = false;

		void initR_();
		void drawR_();
	};

	/// texture
	using FigureTextureContent = FigureContent<VertexDataTexture>;
	using SharedFigureTextureContent = std::shared_ptr<FigureTextureContent>;

	class FigureTexture : public Figure, public PartWithFlip, public PartWithTexture
	{
	public:
		FigureTexture();

		void setFigureContent(SharedFigureTextureContent content) { m_content = content; }

		void setContentSize(Size2 size);
		
		Size2 getContentSize();

		void setShaderSelector(ShaderKitTexture::SelectFunction s) { m_shaders = s; }

		auto getShaderSelector() { return m_shaders; }

		/// update and draw
		virtual void updateContent() override;

		virtual void draw() override;

	protected:

		SharedFigureTextureContent m_content;

		ShaderKitTexture::SelectFunction m_shaders;

		Vec4 m_texture_rect_converted;

		void initR_();
		void drawR_();
	};

	class FigureLoader {
	public:
		static FigureLoader * Get();

		SharedFigureTextureContent createPolygon(size_t edge_count, float start_angle);
		SharedFigureTextureContent createWheel(size_t edge_count, float startD, float nearR, float farR);
		SharedFigureTextureContent createStar(size_t edge_count, float startD, float nearR, float farR);
	};

	class Wheel : public FigureTexture {
	public:

	};

	class Star : public FigureTexture {
	public:

	};
}}
